package cn.itsource.ymcc.controller;

import cn.itsource.ymcc.domain.CourseDoc;
import cn.itsource.ymcc.mapper.HighlightResultMapper;
import cn.itsource.ymcc.query.CourseDocQuery;
import cn.itsource.ymcc.repository.CourseElasticsearchRepository;
import cn.itsource.ymcc.result.AggrPageList;
import cn.itsource.ymcc.result.AggrTermsBucket;
import cn.itsource.ymcc.result.JsonResult;
import cn.itsource.ymcc.result.PageList;
import com.alibaba.fastjson.JSON;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.stream.Collectors;

@RestController
public class CourseElasticsearchController {
    //提供什么些方法  crud
    @Autowired
    private CourseElasticsearchRepository cer;
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;
    @Autowired
    private HighlightResultMapper highlightResultMapper;

    @PostMapping("/findCourseDocByCourseIds")
    public JsonResult findCourseDocByCourseIds(@RequestBody List<Long> courseIds){
        Iterable<CourseDoc> allById = cer.findAllById(courseIds);
        return JsonResult.success().setData(allById);
    }


    @PostMapping("/saveCourse")
    public JsonResult saveCourse(@RequestBody CourseDoc courseDoc){
        //就是别人给我提供好的  已经实现常见的方法  save既可以添加 也可以修改
        cer.save(courseDoc);
        return JsonResult.success();
    }

    @DeleteMapping("/delCourse/{id}")
    public JsonResult delCourse(@PathVariable("id")Long id){
        //就是别人给我提供好的  已经实现常见的方法  save既可以添加 也可以修改
        cer.deleteById(id);
        return JsonResult.success();
    }

    @GetMapping("/findCourseDocByCourseId/{courseId}")
    public JsonResult findCourseDocByCourseId(@PathVariable("courseId")Long courseId){
        //?????   Optional java8的新特性  自己下去了解  重要   号称Optional 优雅判断null
        Optional<CourseDoc> byId = cer.findById(courseId);
        return JsonResult.success().setData(JSON.toJSONString(byId.get()));
    }

    /**
     * 明天实现
     * @param courseDoc
     * @return
     */
    @PostMapping("/queryCourse")
    public JsonResult queryCourse(@RequestBody CourseDocQuery courseDoc){
        //最大的那个查询 _search   设计模式 建造者模式 点点点
        NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder();
        //第一层QueryBuilders  query   BoolQueryBuilder == bool
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //如果 keyword 有值我才拼接这个查询条件
        if(StringUtils.hasText(courseDoc.getKeyword())){
            boolQueryBuilder.filter().add(QueryBuilders.matchQuery("name",courseDoc.getKeyword()));
        }
        if(StringUtils.hasText(courseDoc.getCourseTypeName())){
            boolQueryBuilder.filter().add(QueryBuilders.termQuery("courseTypeName",courseDoc.getCourseTypeName()));
        }


        if(courseDoc.getGradeId() != null){
            boolQueryBuilder.filter().add(QueryBuilders.termQuery("gradeId", courseDoc.getGradeId()));
        }
        if (StringUtils.hasText(courseDoc.getGradeName())){
            boolQueryBuilder.filter().add(QueryBuilders.termQuery("gradeName", courseDoc.getGradeName()));
        }
        if(courseDoc.getPriceMin() != null){
            boolQueryBuilder.filter().add(QueryBuilders.rangeQuery("price").gte(courseDoc.getPriceMin()));
        }
        if(courseDoc.getPriceMax() != null){
            boolQueryBuilder.filter().add(QueryBuilders.rangeQuery("price").lte(courseDoc.getPriceMax()));
        }

        if(StringUtils.hasText(courseDoc.getTenantName())){
            boolQueryBuilder.filter().add(QueryBuilders.matchQuery("gradeName", courseDoc.getTenantName()));
        }


        //排序
        if(StringUtils.hasText(courseDoc.getOrderField())){
            SortOrder sortOrder = SortOrder.ASC;
            if("desc".equalsIgnoreCase(courseDoc.getOrderType())){
                sortOrder = SortOrder.DESC;
            }
            //根据哪一个字段进行排序 前端传给我
            SortBuilder sortBuilder = SortBuilders.fieldSort(courseDoc.getOrderField()).order(sortOrder);
            searchQueryBuilder.withSort(sortBuilder);
        }

        //分页
        //为什么要-1是因为我们的es他的起始页从0开始的
        Pageable pageable = PageRequest.of(courseDoc.getPageNo()-1,courseDoc.getPageSize());
        searchQueryBuilder.withPageable(pageable);

        //设置高亮
        HighlightBuilder.Field  highlightField = new HighlightBuilder.Field("name")
                .preTags("<span style='color:red'>")
                .postTags("</span>");
        searchQueryBuilder.withHighlightFields(highlightField);
        //设置获取聚合
        //聚合查询,按照机构名字聚合，安装数量倒排
        searchQueryBuilder.addAggregation(AggregationBuilders
                .terms("gradeNameAgg") //聚合名字  返回给前端使用
                .field("gradeName.keyword")	//聚合的字段   等级
                .order(BucketOrder.count(false)).size(20));//排序后取前20条
        searchQueryBuilder.addAggregation(AggregationBuilders
                .terms("forUserAgg") //聚合名字  返回给前端使用
                .field("forUser.keyword")	//聚合的字段   等级
                .order(BucketOrder.count(false)).size(20));//排序后取前20条


        searchQueryBuilder.withQuery(boolQueryBuilder);
        //得到查询结果
        NativeSearchQuery build = searchQueryBuilder.build();
//        Page<CourseDoc> page = cer.search(build);
        AggregatedPage<CourseDoc> page = elasticsearchRestTemplate.queryForPage(build, CourseDoc.class, highlightResultMapper);

        System.out.println("当前页码："+page.getNumber());
        System.out.println("每页条数："+page.getSize());
        System.out.println("总条数："+page.getTotalElements());
        System.out.println("总页数："+page.getTotalPages());
        System.out.println("当前页条数："+page.getNumberOfElements());
        //封装返回数据

        //取 聚合
        Map<String, List<AggrTermsBucket>> aggrResult = getAggrResult(page);
        AggrPageList<CourseDoc> courseDocAggrPageList = new AggrPageList<CourseDoc>(page.getTotalElements(), page.getContent(),aggrResult);
        return JsonResult.success().setData(courseDocAggrPageList);
    }

    //构造聚合返回结果
    private Map<String, List<AggrTermsBucket>> getAggrResult(AggregatedPage<CourseDoc> page) {
        //用来封装聚合结果
        Map<String,List<AggrTermsBucket>> aggrMap = new HashMap<>();

        //遍历聚合结果
        Aggregations aggregations = page.getAggregations();

        Map<String, Aggregation> stringAggregationMap = aggregations.asMap();

        Set<Map.Entry<String, Aggregation>> entries = stringAggregationMap.entrySet();

        entries.forEach(e->{
            //集合的名字
            String key = e.getKey();
            //集合的值的处理
            ParsedStringTerms value = (ParsedStringTerms)e.getValue();
            List<? extends Terms.Bucket> buckets = value.getBuckets();

            //使用lomda 把  List<StringTerms.Bucket>  转成  List<AggrTermsVo>
            List<AggrTermsBucket> aggrTermsVos = buckets.stream().map(bucket -> {
                return new AggrTermsBucket(bucket.getKeyAsString(), bucket.getDocCount());
            }).collect(Collectors.toList());

            aggrMap.put(key, aggrTermsVos);
        });
        return aggrMap;
    }
}
